#!/bin/bash
# change directory scripts location
cd "$(dirname "$0")"
########################################
set -x  # output all commands
set -o pipefail
set -e # exit on immediately on every error
set -u # error on usage of undefined variables
########################################

# Set the bashrc to setup derived env variables
source /etc/bash.bashrc

function sigterm_handler() {
  echo "SIGTERM signal received, try to gracefully shutdown all services..."
  gitlab-ctl stop
  exit
}
trap "sigterm_handler; exit" TERM

log() {
  echo "### $(date +%Y-%m-%d\ %H:%M:%S) ### $1"
}

checkGitlabPort() {
  curl --silent --output /dev/null -w ''%{http_code}'' "$INSTANCE_HOST":"$GITLAB_PORT"/"${1-}"
}

recreateGitlabAdminToken() {
  # http://gitlab.com/help/administration/troubleshooting/gitlab_rails_cheat_sheet.md
  # Deleting all API tokens for root user (id=1)
  gitlab-rails runner -e production "
    User.find(1).personal_access_tokens.each do |cur|
      cur.delete
    end
  "
  #Creating Admin API token $GITLAB_ADMIN_TOKEN. This might take up to 5 minutes
  gitlab-rails runner -e production "User.find(1).personal_access_tokens.create(
    name: 'mlreef-backend-api-token $(date +%Y-%m-%d\ %H:%M:%S)',
    token_digest: Gitlab::CryptoHelper.sha256('$GITLAB_ADMIN_TOKEN'),
    impersonation: false,
    scopes: [:api,:sudo]
  )"
}

getGitlabRunnerRegistrationToken() {
  gitlab-rails runner -e production "puts Gitlab::CurrentSettings.current_application_settings.runners_registration_token" | tr -d '\r'
}

# Set max_artifacts_size using api
setMaxArtifactSizeForGitlab() {
  curl --request PUT --header "PRIVATE-TOKEN: $GITLAB_ADMIN_TOKEN" "${INSTANCE_HOST}:${GITLAB_PORT}"/"api/v4/application/settings?max_artifacts_size=5120"
}

# Set max_attachment_size using api
setMaxAttachmentSizeForGitlab() {
  curl --request PUT --header "PRIVATE-TOKEN: $GITLAB_ADMIN_TOKEN" "${INSTANCE_HOST}:${GITLAB_PORT}"/"api/v4/application/settings?max_attachment_size=5120"
}

# Set container_registry_token_expire_delay api
setContainerRegistryTokenExpireDelayForGitlab() {
  curl --request PUT --header "PRIVATE-TOKEN: $GITLAB_ADMIN_TOKEN" "${INSTANCE_HOST}:${GITLAB_PORT}"/"api/v4/application/settings?container_registry_token_expire_delay=30"
}


#########################
# Call the Gitlab wrapper
# Gitlab will self-daemonize and run in the background hence no '&' is needed
log "booting and configuring Gitlab ..."
/assets/gitlab-wrapper >/dev/null

log "Ensuring availability of the Gitlab API to start"
until [ "$(checkGitlabPort /api/v4/projects)" = "200" ]; do
  printf '.'
  sleep 5;
done
log "Expecting code 200; received: $(checkGitlabPort /api/v4/projects)"
log "Waiting for Gitlab Runners API. The runners API is running in a separate process from the normal API"
until [ "$(checkGitlabPort /runners)" = "302" ]; do
  printf '.'
  sleep 5;
done
log "Expecting code 302; received: $(checkGitlabPort /runners)"

log "Refreshing root user api-token"
recreateGitlabAdminToken

#########################
# Gitlab setting changes
log "Changing gitlab settings"
setMaxArtifactSizeForGitlab
setMaxAttachmentSizeForGitlab
setContainerRegistryTokenExpireDelayForGitlab

log "Getting Gitlab runners registration token from Gitlab."
RUNNER_REGISTRATION_TOKEN=$(getGitlabRunnerRegistrationToken)

log "GITLAB_RUNNER_REGISTRATION_TOKEN=$RUNNER_REGISTRATION_TOKEN"
log "Gitlab is ready"

#########################
# Setup gitlab-runner

DOCKER_NETWORK=$(docker inspect $(hostname) -f "{{json .NetworkSettings.Networks}}" | jq -r 'keys[0]')  

# Wait for Gitlab to enable the database
touch /var/log/configuration.lock
touch /var/log/configuration.log
{
  echo "### Configuring gitlab runner for localhost:$GITLAB_PORT"
  # Clear runner config
  echo '' > /etc/gitlab-runner/config.toml
  sleep 10 # This sleep is necessary to allow the gitlab Runner to reload the now empty config file
  CONTAINER_IP=$(hostname -I | awk '{print $1}')
  gitlab-runner register --non-interactive            \
    --url="http://${INSTANCE_HOST}:${GITLAB_PORT}/"   \
    --docker-network-mode ${DOCKER_NETWORK}           \
    --registration-token "$RUNNER_REGISTRATION_TOKEN" \
    --executor "docker" \
    --docker-image alpine:latest \
    --docker-privileged="true"   \
    --description "Packaged Runner"  \
    --tag-list "docker,local-docker" \
    --run-untagged="true" \
    --locked="false" \
    --access-level="not_protected"   \
    --docker-extra-hosts "$INSTANCE_HOST:$CONTAINER_IP" \
    --docker-pull-policy always                         \
    --docker-pull-policy if-not-present

  gitlab-runner start

} >/var/log/configuration.log
rm -f /var/log/configuration.lock

##################################
# Local registry EPF image upload
# Setup mlreef project
# Gitlab local group and project creation
export GITLAB_ROOTGROUP="$MLREEF_GITLAB_LOCAL_REGISTRY_GROUP"
export GITLAB_PROJECT="$MLREEF_GITLAB_LOCAL_REGISTRY_PROJECT"

# Creating root group
rootGroupId=$(curl --header "PRIVATE-TOKEN: $GITLAB_ADMIN_TOKEN" "${INSTANCE_HOST}:${GITLAB_PORT}/api/v4/groups?search=$GITLAB_ROOTGROUP" | jq -M 'map(select(.name == "'$GITLAB_ROOTGROUP'"))| .[0].id' )
if [ $rootGroupId == "null" ]; then
  rootGroupId=$(curl --request POST --header "PRIVATE-TOKEN: $GITLAB_ADMIN_TOKEN" --header "Content-Type: application/json" \
  --data '{"path": "'$GITLAB_ROOTGROUP'", "name": "'$GITLAB_ROOTGROUP'", "visibility": "public" ,"lfs_enabled": "true" , "description": "Root Group" }' \
  "${INSTANCE_HOST}:${GITLAB_PORT}/api/v4/groups/" | jq '.["id"]')
fi
echo "Root group Id: $rootGroupId"
# Project creation
projectId=$(curl "${INSTANCE_HOST}:${GITLAB_PORT}/api/v4/groups/$rootGroupId/projects?search=$GITLAB_PROJECT" -H "PRIVATE-TOKEN: $GITLAB_ADMIN_TOKEN" | jq -M 'map(select(.name == "'$GITLAB_PROJECT'"))| .[0].id'  )
if [ $projectId == "null" ]; then
  projectId=$(curl --request POST --header "PRIVATE-TOKEN: $GITLAB_ADMIN_TOKEN" --header "Content-Type: application/json" \
  --data '{"path": "'$GITLAB_PROJECT'", "namespace_id": "'$rootGroupId'", "visibility": "public"}' \
  "${INSTANCE_HOST}:${GITLAB_PORT}/api/v4/projects/" | jq '.["id"]')
fi
echo "Project Id: $projectId" 

log "Default mlreef project created"

# Local EPF image upload
export MLREEF_DOCKER_REGISTRY_USER=root

if [ -n "${MLREEF_DOCKER_REGISTRY_EXTERNAL_URL+x}" ] && [ -n "${MLREEF_DOCKER_REGISTRY_USER+x}" ] && [ -n "${GITLAB_ADMIN_TOKEN+x}" ]; then
          echo "$GITLAB_ADMIN_TOKEN" | docker login "$MLREEF_DOCKER_REGISTRY_EXTERNAL_URL" --username="$MLREEF_DOCKER_REGISTRY_USER" --password-stdin
fi

## The OFFLINE_MODE variable is not set by default, so the images for local repo ar created at runtime and get pushed
## When the OFFLINE_MODE variable is set, it means the server is offline, in that case the images are uploaded later 
## , when the container is up and running and get pushed to registry
if [[ -z ${OFFLINE_MODE:-} ]] ; then
docker build --tag "$LOCAL_EXPERIMENT_IMAGE_PATH" -f ../images/experiment/Dockerfile ../images/experiment
docker push "$LOCAL_EXPERIMENT_IMAGE_PATH"
log "$LOCAL_EXPERIMENT_IMAGE_PATH has been successfully uploaded "

docker build --tag "$LOCAL_EPF_IMAGE_PATH" -f ../epf/Dockerfile ../epf
docker push "$LOCAL_EPF_IMAGE_PATH"
log "$LOCAL_EPF_IMAGE_PATH has been successfully uploaded "
fi

#########################
# Setup the PostgreSQL mlreefdb server
# Postgres will self-daemonize and run in the background hence no '&' is needed
log "Setting up MLReef database"
/assets/mlreefdb-setup

#########################
# Start the backend
# using "&" to run the process in the background
log "starting MLReef backend..."
java -cp /app:/app/lib/* com.mlreef.rest.RestApplicationKt &

########################
# Start the API Gateway
# Update nginx default conf with the current parameters
sed -i "s/gitlab:10080/localhost:$GITLAB_PORT/" /etc/nginx/conf.d/default.conf
sed -i "s/backend:8080/localhost:$MLREEF_BACKEND_PORT/" /etc/nginx/conf.d/default.conf
# NGINX will self-daemonize and run in the background hence no '&' is needed
/usr/sbin/nginx

########################
# keep Docker container alive
tail -f /dev/null
# Wait for SIGTERM
wait

log "mlreef-wrapper is finished" 

#Bring the first process to foreground
fg %1

